package com.untour.im.server.config;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.untour.im.codec.protocol.handler.HeartBeatServerHandler;
import com.untour.im.codec.protocol.handler.ImIdleStateHandler;
import com.untour.im.codec.protocol.handler.RequestMessageDecoderHandler;
import com.untour.im.codec.protocol.handler.ResponseMessageEncoderHandler;
import com.untour.im.server.handler.CustomExceptionHandler;
import com.untour.im.server.handler.HeartBeatEventHandler;
import com.untour.im.server.handler.LoginRequestHandler;
import com.untour.im.server.handler.MessageRequestHandler;
import com.untour.im.server.service.LoginService;
import com.untour.im.server.service.UserChannelService;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;

@Slf4j
@Component
public class LinkServer {
    private Channel channel;

    /**
     * bossGroup连接线程组，主要负责接受客户端连接，一般一个线程足矣
     */
    private EventLoopGroup boosGroup;
    /**
     * workerGroup工作线程组，主要负责网络IO读写
     */
    private EventLoopGroup workGroup;

    @Resource
    private NacosDiscoveryProperties nacosDiscoveryProperties;

    @Resource
    private ImConfigProperties imConfigProperties;

    @Resource
    private LoginService loginService;

    @Resource
    private UserChannelService userChannelService;

    public void start(){
        log.info("=================Netty 端口启动:{}==================", imConfigProperties.getTcpPort());
        //绑定端口
        ServerBootstrap bootstrap = new ServerBootstrap();
        boosGroup = new NioEventLoopGroup(imConfigProperties.getBossThreadSize());
        workGroup = new NioEventLoopGroup(imConfigProperties.getWorkThreadSize());
        bootstrap.group(boosGroup,workGroup)
                // 非阻塞异步服务端TCP Socket 连接
                .channel(NioServerSocketChannel.class)
                // 服务端可连接队列大小
                .option(ChannelOption.SO_BACKLOG, imConfigProperties.getSoBacklog())
                // 参数表示允许重复使用本地地址和端口
                .option(ChannelOption.SO_REUSEADDR, true)
                // 是否禁用Nagle算法 简单点说是否批量发送数据 true关闭 false开启。 开启的话可以减少一定的网络开销，但影响消息实时性
                .childOption(ChannelOption.TCP_NODELAY, true)
                // 保活开关2h没有数据服务端会发送心跳包
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                //初始化handler
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        log.info("=================收到新的链接:{}==================", ch.localAddress());
                        ch.pipeline().addLast(new ImIdleStateHandler());
                        ch.pipeline().addLast(new RequestMessageDecoderHandler());
                        ch.pipeline().addLast(new HeartBeatEventHandler(imConfigProperties.getHeartBeatTime(),userChannelService));
                        ch.pipeline().addLast(new ResponseMessageEncoderHandler());
                        ch.pipeline().addLast(new CustomExceptionHandler());
                        ch.pipeline().addLast(new LoginRequestHandler(loginService,userChannelService));
                        ch.pipeline().addLast(new HeartBeatServerHandler());
                        ch.pipeline().addLast(new MessageRequestHandler());
                    }
                });
        //绑定Netty的启动端口
        channel = bootstrap.bind(imConfigProperties.getTcpPort()).channel();

        //todo 注册到nacos中
    }


    @PreDestroy
    public void destroy() {
        //todo 可以通知客户端重新连接
        log.error("=================Netty服务关闭==================");
        if (channel != null) {
            channel.close();
        }
        boosGroup.shutdownGracefully();
        workGroup.shutdownGracefully();
    }
}
